package cc.owoo.godpen.network.http;

import cc.owoo.godpen.analysis.html.Document;
import cc.owoo.godpen.analysis.json.Json;
import cc.owoo.godpen.util.N;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.LinkedList;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.InflaterInputStream;
import java.util.zip.InflaterOutputStream;

/**
 * HTTP工具类
 * Created by nimensei
 * 2022-02-24 下午 05:54
 **/
public class HttpUtil {
    public static String encode = "UTF-8";// 全局默认编码

    /**
     * 序列化请求/响应头
     *
     * @param string 需要拼接的字符串
     * @param header 头
     */
    public static void stringifyHeader(StringBuilder string, cc.owoo.godpen.network.http.AbstractHeader header) {
        LinkedList<String[]> headerList = new LinkedList<>();
        header.forHeader((key, value) -> headerList.add(new String[]{key, value}));
        headerList.sort((a, b) -> a[0].compareTo(b[0]));
        if (headerList.size() == 0)
            string.append("null").append('\n');
        else
            for (String[] array : headerList)
                string.append(array[0]).append(": ").append(array[1]).append('\n');
    }

    /**
     * 序列化数据：将byte数组转换成字符串，如果byte数组的长度过长则拼接byte数组的长度
     *
     * @param string  需要拼接的字符串
     * @param content byte数组
     * @param header  头部信息
     */
    static void stringifyContent(StringBuilder string, byte[] content, cc.owoo.godpen.network.http.AbstractHeader header) {
        if (content == null) {
            string.append("null");
            return;
        }
        ContentType contentType = header.contentType;
        string.append("content size: ").append(N.size(content.length));
        if (contentType.isText()) {
            String text = getText(content, header);
            String[] array = text.split("\n");
            string.append(", text line: ").append(array.length);
            string.append(", text length: ").append(text.length()).append('\n');
            int len = Math.min(array.length, 50);
            for (int i = 0; i < len; i++) {
                if (i > 0)
                    string.append('\n');
                string.append(array[i]);
            }
            if (len < array.length)
                string.append('\n').append("......");
        } else if (contentType.isImage()) {
            BufferedImage image = getImage(content, header);
            if (image != null)
                string.append(", image size: ").append(image.getWidth()).append(" * ").append(image.getHeight());
        }
    }

    /**
     * 获取内容编码
     *
     * @param header 头部信息
     * @return 内容编码
     */
    public static String getCharset(cc.owoo.godpen.network.http.AbstractHeader header) {
        if (header == null)
            return null;
        String charset = header.contentType.getProperty("charset");
        if (charset == null)
            charset = header.encode;
        return charset;
    }

    /**
     * 获取文本内容
     *
     * @param content 内容数据
     * @param header  头部信息
     * @return 将字节数组转换成文本，可能为空
     */
    public static String getText(byte[] content, cc.owoo.godpen.network.http.AbstractHeader header) {
        if (content == null)
            return null;
        String charset = getCharset(header);
        try {
            return new String(content, charset);
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }

    /**
     * 获取Json对象
     *
     * @param content 内容数据
     * @param header  头部信息
     * @return 将字节数组转换Json对象
     */
    public static Json getJson(byte[] content, cc.owoo.godpen.network.http.AbstractHeader header) {
        return Json.parse(getText(content, header));
    }


    /**
     * 获取Document对象
     *
     * @param content 内容数据
     * @param header  头部信息
     * @return 将字节数组转换Document对象
     */
    public static Document getDocument(byte[] content, cc.owoo.godpen.network.http.AbstractHeader header) {
        return Document.parse(getText(content, header));
    }

    /**
     * 获取Image对象
     *
     * @param content 内容数据
     * @param header  头部信息
     * @return 将字节数组转换Image对象，可能为空
     */
    public static BufferedImage getImage(byte[] content, AbstractHeader header) {
        if (content == null)
            return null;
        try {
            return ImageIO.read(new ByteArrayInputStream(content));
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 格式化请求头字段
     *
     * @param value 键（key）
     * @return 格式化后的键（key）
     */
    public static String formatHeader(String value) {
        if (value == null)
            return null;
        value = value.trim().toLowerCase();
        if (value.length() == 0)
            return null;
        return value;
    }

    /**
     * 将键值对对象转换成URL参数字符串
     *
     * @param params 键值对
     * @return URL参数字符串
     */
    public static String paramsToUrl(Map<String, String> params) {
        StringBuilder string = new StringBuilder();
        params.forEach((key, value) -> {
            if (string.length() > 0)
                string.append('&');
            string.append(urlEncode(key)).append('=').append(urlEncode(value));
        });
        return string.toString();
    }

    /**
     * 数据编码
     *
     * @param data   需要编码的字节数据
     * @param encode 编码格式
     * @return 编码后的数据
     * @throws IOException 处理异常
     */
    public static byte[] dataEncode(byte[] data, String encode) throws IOException {
        if (data == null || encode == null || (encode = encode.trim()).length() == 0)
            return data;
        encode = encode.toLowerCase();
        ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
        OutputStream output = switch (encode) {
            case "gzip" -> new GZIPOutputStream(byteArray);
            case "deflate" -> new InflaterOutputStream(byteArray);
            default -> null;
        };
        if (output == null)
            return data;
        output.write(data);
        output.flush();
        return byteArray.toByteArray();
    }

    /**
     * 数据解码
     *
     * @param data   需要解码的字节数据
     * @param encode 解码格式
     * @return 解码后的数据
     * @throws IOException 处理异常
     */
    public static byte[] dataDecode(byte[] data, String encode) throws IOException {
        if (data == null || encode == null || (encode = encode.trim()).length() == 0)
            return data;
        encode = encode.toLowerCase();
        InputStream input = new ByteArrayInputStream(data);
        input = switch (encode) {
            case "gzip" -> new GZIPInputStream(input);
            case "deflate" -> new InflaterInputStream(input);
            default -> null;
        };
        if (input == null)
            return data;
        ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
        int len;
        byte[] cache = new byte[1024];
        while ((len = input.read(cache)) != -1)
            byteArray.write(cache, 0, len);
        return byteArray.toByteArray();
    }

    /**
     * 编码GZIP
     *
     * @param data 需要编码的字节数据
     * @return 编码后的字节数据
     * @throws IOException 处理异常
     */
    public static byte[] gzipEncode(byte[] data) throws IOException {
        return dataEncode(data, "gzip");
    }

    /**
     * GZIP解码
     *
     * @param data 需要解码字节数据
     * @return 解码后的字节数据
     * @throws IOException 处理异常
     */
    public static byte[] gzipDecode(byte[] data) throws IOException {
        return dataDecode(data, "gzip");
    }

    /**
     * URL编码
     *
     * @param value 需要编码的值
     * @param enc   值的字符编码
     * @return URL编码后的值
     */
    public static String urlEncode(String value, String enc) {
        try {
            return java.net.URLEncoder.encode(value, enc);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * URL编码
     *
     * @param value 需要编码的值
     * @return URL编码后的值
     */
    public static String urlEncode(String value) {
        return urlEncode(value, encode);
    }

    /**
     * URL解码
     *
     * @param value 需要解码的值
     * @param enc   值的字符编码
     * @return URL解码后的值
     */
    public static String urlDecode(String value, String enc) {
        try {
            return java.net.URLDecoder.decode(value, enc);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * URL解码
     *
     * @param value 需要解码的值
     * @return URL解码后的值
     */
    public static String urlDecode(String value) {
        return urlDecode(value, encode);
    }


    /**
     * 根据后最获取内容类型
     *
     * @param postfix        后缀
     * @param defaultPostfix 默认后缀
     * @return 内容类型
     */
    public static String getContentType(String postfix, String defaultPostfix) {
        return switch (postfix) {
            case ".pdx" -> "application/vnd.adobe.pdx";
            case ".xdp" -> "application/vnd.adobe.xdp";
            case ".mpeg" -> "video/mpg";
            case ".xdr" -> "text/xml";
            case ".wsc" -> "text/scriptlet";
            case ".mns" -> "audio/x-musicnet-stream";
            case ".pdf" -> "application/pdf";
            case ".awf" -> "application/vnd.adobe.workflow";
            case ".mp3" -> "audio/mp3";
            case ".wrk" -> "application/x-wrk";
            case ".mp4" -> "video/mpeg4";
            case ".mp1" -> "audio/mp1";
            case ".wax" -> "audio/x-ms-wax";
            case ".wri" -> "application/x-wri";
            case ".mp2" -> "audio/mp2";
            case ".gbr" -> "application/x-gbr";
            case ".mpg" -> "video/mpg";
            case ".mpe" -> "video/x-mpeg";
            case ".nrf" -> "application/x-nrf";
            case ".frm" -> "application/x-frm";
            case ".mpd" -> "application/vnd.ms-project";
            case ".epi" -> "application/x-epi";
            case ".mpa" -> "video/x-mpg";
            case ".mpp" -> "application/vnd.ms-project";
            case ".sld" -> "application/x-sld";
            case ".eps" -> "application/postscript";
            case ".slb" -> "application/x-slb";
            case ".class" -> "java/*";
            case "." -> "application/x-";
            case ".mp2v" -> "video/mpeg";
            case ".igs" -> "application/x-igs";
            case ".xslt" -> "text/xml";
            case ".biz" -> "text/xml";
            case ".ls" -> "application/x-javascript";
            case ".mpv2" -> "video/mpeg";
            case ".fax" -> "image/fax";
            case ".jfif" -> "image/jpeg";
            case ".xwd" -> "application/x-xwd";
            case ".cml" -> "text/xml";
            case ".pfx" -> "application/x-pkcs12";
            case ".aif" -> "audio/aiff";
            case ".hgl" -> "application/x-hgl";
            case ".mi" -> "application/x-mi";
            case ".dot" -> "application/msword";
            case ".iii" -> "application/x-iphone";
            case ".cmp" -> "application/x-cmp";
            case ".vsd" -> "application/vnd.visio";
            case ".mpga" -> "audio/rn-mpeg";
            case ".mpw" -> "application/vnd.ms-project";
            case ".mpx" -> "application/vnd.ms-project";
            case ".slk" -> "drawing/x-slk";
            case ".mpv" -> "video/mpg";
            case ".mps" -> "video/x-mpeg";
            case ".mpt" -> "application/vnd.ms-project";
            case ".odc" -> "text/x-ms-odc";
            case ".rjt" -> "application/vnd.rn-realsystem-rjt";
            case ".rjs" -> "application/vnd.rn-realsystem-rjs";
            case ".doc" -> "application/msword";
            case ".xfd" -> "application/vnd.adobe.xfd";
            case ".html" -> "text/html";
            case ".wbmp" -> "image/vnd.wap.wbmp";
            case ".rle" -> "application/x-rle";
            case ".fdf" -> "application/vnd.fdf";
            case ".rlc" -> "application/x-rlc";
            case ".snd" -> "audio/basic";
            case ".xquery" -> "text/xml";
            case ".pgl" -> "application/x-pgl";
            case ".top" -> "drawing/x-top";
            case ".pwz" -> "application/vnd.ms-powerpoint";
            case ".rsml" -> "application/vnd.rn-rsml";
            case ".smk" -> "application/x-smk";
            case ".cmx" -> "application/x-cmx";
            case ".out" -> "application/x-out";
            case ".mac" -> "application/x-mac";
            case ".smi" -> "application/smil";
            case ".la1" -> "audio/x-liquid-file";
            case ".man" -> "application/x-troff-man";
            case ".vsw" -> "application/vnd.visio";
            case ".vsx" -> "application/vnd.visio";
            case ".vcf" -> "text/x-vcard";
            case ".vss" -> "application/vnd.visio";
            case ".vst" -> "application/vnd.visio";
            case ".bmp" -> "application/x-bmp";
            case ".mhtml" -> "message/rfc822";
            case ".p7s" -> "application/pkcs7-signature";
            case ".pic" -> "application/x-pic";
            case ".vdx" -> "application/vnd.visio";
            case ".p7r" -> "application/x-pkcs7-certreqresp";
            case ".rmf" -> "application/vnd.adobe.rmf";
            case ".cot" -> "application/x-cot";
            case ".math" -> "text/xml";
            case ".p7m" -> "application/pkcs7-mime";
            case ".301" -> "application/x-301";
            case ".vda" -> "application/x-vda";
            case ".smil" -> "application/smil";
            case ".p7c" -> "application/pkcs7-mime";
            case ".lavs" -> "audio/x-liquid-secure";
            case ".vtx" -> "application/vnd.visio";
            case ".p7b" -> "application/x-pkcs7-certificates";
            case ".wvx" -> "video/x-ms-wvx";
            case ".aiff" -> "audio/aiff";
            case ".aifc" -> "audio/aiff";
            case ".pl" -> "application/x-perl";
            case ".mdb" -> "application/msaccess";
            case ".dbf" -> "application/x-dbf";
            case ".drw" -> "application/x-drw";
            case ".spc" -> "application/x-pkcs7-certificates";
            case ".pr" -> "application/x-pr";
            case ".etd" -> "application/x-ebx";
            case ".ps" -> "application/postscript";
            case ".rmp" -> "application/vnd.rn-rn_music_package";
            case ".m1v" -> "video/x-mpeg";
            case ".rmm" -> "audio/x-pn-realaudio";
            case ".sol" -> "text/plain";
            case ".rmi" -> "audio/mid";
            case ".rmj" -> "application/vnd.rn-realsystem-rmj";
            case ".lar" -> "application/x-laplayer-reg";
            case ".net" -> "image/pnetvue";
            case ".rmx" -> "application/vnd.rn-realsystem-rmx";
            case ".rms" -> "application/vnd.rn-realmedia-secure";
            case ".sor" -> "text/plain";
            case ".gif" -> "image/gif";
            case ".dcd" -> "text/xml";
            case ".tsd" -> "text/xml";
            case ".img" -> "application/x-img";
            case ".nws" -> "message/rfc822";
            case ".bot" -> "application/x-bot";
            case ".323" -> "text/h323";
            case ".spp" -> "text/xml";
            case ".m2v" -> "video/x-mpeg";
            case ".mtx" -> "text/xml";
            case ".ai" -> "application/postscript";
            case ".dbm" -> "application/x-dbm";
            case ".edn" -> "application/vnd.adobe.edn";
            case ".spl" -> "application/futuresplash";
            case ".sisx" -> "application/vnd.symbian.install";
            case ".rnx" -> "application/vnd.rn-realplayer";
            case ".dbx" -> "application/x-dbx";
            case ".ra" -> "audio/vnd.rn-realaudio";
            case ".lbm" -> "application/x-lbm";
            case ".crt" -> "application/x-x509-ca-cert";
            case ".au" -> "audio/basic";
            case ".wk4" -> "application/x-wk4";
            case ".jpg" -> "image/jpeg";
            case ".m4e" -> "video/mpeg4";
            case ".jpe" -> "image/jpeg";
            case ".sam" -> "application/x-sam";
            case ".wk3" -> "application/x-wk3";
            case ".gl2" -> "application/x-gl2";
            case ".rm" -> "application/vnd.rn-realmedia";
            case ".ltr" -> "application/x-ltr";
            case ".plg" -> "text/html";
            case ".rp" -> "image/vnd.rn-realpix";
            case ".wiz" -> "application/msword";
            case ".anv" -> "application/x-anv";
            case ".sat" -> "application/x-sat";
            case ".dtd" -> "text/xml";
            case ".pko" -> "application/vnd.ms-pki.pko";
            case ".rt" -> "text/vnd.rn-realtext";
            case ".rv" -> "video/vnd.rn-realvideo";
            case ".midi" -> "audio/mid";
            case ".cal" -> "application/x-cals";
            case ".m3u" -> "audio/mpegurl";
            case ".crl" -> "application/pkix-crl";
            case ".dcx" -> "application/x-dcx";
            case ".cat" -> "application/vnd.ms-pki.seccat";
            case ".mocha" -> "application/x-javascript";
            case ".css" -> "text/css";
            case ".fif" -> "application/fractals";
            case ".xlw" -> "application/x-xlw";
            case ".906" -> "application/x-906";
            case ".907" -> "drawing/907";
            case ".xls" -> "application/vnd.ms-excel";
            case ".hmr" -> "application/x-hmr";
            case ".ssm" -> "application/streamingmedia";
            case ".ins" -> "application/x-internet-signup";
            case ".rpm" -> "audio/x-pn-realaudio-plugin";
            case ".plt" -> "application/x-plt";
            case ".csi" -> "application/x-csi";
            case ".tdf" -> "application/x-tdf";
            case ".mfp" -> "application/x-shockwave-flash";
            case ".pls" -> "audio/scpls";
            case ".ras" -> "application/x-ras";
            case ".cdf" -> "application/x-netcdf";
            case ".mxp" -> "application/x-mmxp";
            case ".rat" -> "application/rat-file";
            case ".tg4" -> "application/x-tg4";
            case ".ram" -> "audio/x-pn-realaudio";
            case ".wks" -> "application/x-wks";
            case ".stm" -> "text/html";
            case ".stl" -> "application/vnd.ms-pki.stl";
            case ".jpeg" -> "image/jpeg";
            case ".png" -> "image/png";
            case ".sst" -> "application/vnd.ms-pki.certstore";
            case ".exe" -> "application/x-msdownload";
            case ".der" -> "application/x-x509-ca-cert";
            case ".apk" -> "application/vnd.android.package-archive";
            case ".wkq" -> "application/x-wkq";
            case ".ipa" -> "application/vnd.iphone";
            case ".xml" -> "text/xml";
            case ".lmsff" -> "audio/x-la-lms";
            case ".cut" -> "application/x-cut";
            case ".a11" -> "application/x-a11";
            case ".sdp" -> "application/sdp";
            case ".cel" -> "application/x-cel";
            case ".dgn" -> "application/x-dgn";
            case ".wma" -> "audio/x-ms-wma";
            case ".mid" -> "audio/mid";
            case ".sdw" -> "application/x-sdw";
            case ".jsp" -> "text/html";
            case ".dwg" -> "application/x-dwg";
            case ".wsdl" -> "text/xml";
            case ".dwf" -> "model/vnd.dwf";
            case ".cdr" -> "application/x-cdr";
            case ".001" -> "application/x-001";
            case ".g4" -> "application/x-g4";
            case ".sty" -> "application/x-sty";
            case ".mht" -> "message/rfc822";
            case ".hpl" -> "application/x-hpl";
            case ".rtf" -> "application/msword";
            case ".svg" -> "text/xml";
            case ".wmz" -> "application/x-ms-wmz";
            case ".wmx" -> "video/x-ms-wmx";
            case ".c4t" -> "application/x-c4t";
            case ".wmv" -> "video/x-ms-wmv";
            case ".ppa" -> "application/vnd.ms-powerpoint";
            case ".hpg" -> "application/x-hpgl";
            case ".ppm" -> "application/x-ppm";
            case ".x_t" -> "application/x-x_t";
            case ".gp4" -> "application/x-gp4";
            case ".movie" -> "video/x-sgi-movie";
            case ".pot" -> "application/vnd.ms-powerpoint";
            case ".dxf" -> "application/x-dxf";
            case ".cer" -> "application/x-x509-ca-cert";
            case ".cg4" -> "application/x-g4";
            case ".mil" -> "application/x-mil";
            case ".wmf" -> "application/x-wmf";
            case ".dxb" -> "application/x-dxb";
            case ".tga" -> "application/x-tga";
            case ".wmd" -> "application/x-ms-wmd";
            case ".fo" -> "text/xml";
            case ".uin" -> "application/x-icq";
            case ".x_b" -> "application/x-x_b";
            case ".wml" -> "text/vnd.wap.wml";
            case ".swf" -> "application/x-shockwave-flash";
            case ".wm" -> "video/x-ms-wm";
            case ".asp" -> "text/asp";
            case ".ws" -> "application/x-ws";
            case ".icb" -> "application/x-icb";
            case ".torrent" -> "application/x-bittorrent";
            case ".cgm" -> "application/x-cgm";
            case ".asx" -> "video/x-ms-asf";
            case ".isp" -> "application/x-internet-signup";
            case ".asf" -> "video/x-ms-asf";
            case ".ppt" -> "application/x-ppt";
            case ".rdf" -> "text/xml";
            case ".xpl" -> "audio/scpls";
            case ".pps" -> "application/vnd.ms-powerpoint";
            case ".txt" -> "text/plain";
            case ".asa" -> "text/asa";
            case ".dib" -> "application/x-dib";
            case ".wp6" -> "application/x-wp6";
            case ".tiff" -> "image/tiff";
            case ".prf" -> "application/pics-rules";
            case ".pc5" -> "application/x-pc5";
            case ".xq" -> "text/xml";
            case ".prn" -> "application/x-prn";
            case ".wpd" -> "application/x-wpd";
            case ".xap" -> "application/x-silverlight-app";
            case ".vxml" -> "text/xml";
            case ".ico" -> "image/x-icon";
            case ".vml" -> "text/xml";
            case ".r3t" -> "text/vnd.rn-realtext3d";
            case ".xql" -> "text/xml";
            case ".rec" -> "application/vnd.rn-recording";
            case ".tif" -> "image/tiff";
            case ".p12" -> "application/x-pkcs12";
            case ".red" -> "application/x-red";
            case ".acp" -> "audio/x-mei-aac";
            case ".hqx" -> "application/mac-binhex40";
            case ".p10" -> "application/pkcs10";
            case ".hrf" -> "application/x-hrf";
            case ".latex" -> "application/x-latex";
            case ".*" -> "application/octet-stream";
            case ".wq1" -> "application/x-wq1";
            case ".eml" -> "message/rfc822";
            case ".emf" -> "application/x-emf";
            case ".mml" -> "text/xml";
            case ".rgb" -> "application/x-rgb";
            case ".wb3" -> "application/x-wb3";
            case ".xsd" -> "text/xml";
            case ".wb1" -> "application/x-wb1";
            case ".wb2" -> "application/x-wb2";
            case ".prt" -> "application/x-prt";
            case ".wpl" -> "application/vnd.ms-wpl";
            case ".wpg" -> "application/x-wpg";
            case ".uls" -> "text/iuls";
            case ".c90" -> "application/x-c90";
            case ".IVF" -> "video/x-ivf";
            case ".wr1" -> "application/x-wr1";
            case ".xfdf" -> "application/vnd.adobe.xfdf";
            case ".dll" -> "application/x-msdownload";
            case ".pcx" -> "application/x-pcx";
            case ".mnd" -> "audio/x-musicnet-download";
            case ".htm" -> "text/html";
            case ".sit" -> "application/x-stuffit";
            case ".htx" -> "text/html";
            case ".rmvb" -> "application/vnd.rn-realmedia-vbr";
            case ".tld" -> "text/xml";
            case ".wav" -> "audio/wav";
            case ".java" -> "java/*";
            case ".vpg" -> "application/x-vpeg005";
            case ".ptn" -> "application/x-ptn";
            case ".ent" -> "text/xml";
            case ".htt" -> "text/webviewhtml";
            case ".iff" -> "application/x-iff";
            case ".avi" -> "video/avi";
            case ".hta" -> "application/hta";
            case ".xsl" -> "text/xml";
            case ".cit" -> "application/x-cit";
            case ".js" -> "application/x-javascript";
            case ".sis" -> "application/vnd.symbian.install";
            case ".pcl" -> "application/x-pcl";
            case ".ig4" -> "application/x-g4";
            case ".pci" -> "application/x-pci";
            case ".ws2" -> "application/x-ws";
            case ".htc" -> "text/x-component";
            case ".xhtml" -> "text/html";
            default -> defaultPostfix;
        };
    }
}
